#include "ui.h"
#ifdef __linux__
#include <ncurses.h>
#else
#include "../include/curses.h"
#endif
#include<string.h>
#include<stdlib.h>

#define at 1     //alto ventana de Wtitulo
#define am 1     //alto ventana Wmenu
#define ae 1     //alto ventana de Westado
#define ac (LINES - at - am - ae)   //alto ventana Wcuerpo
#define bw COLS  //ancho ventana Wcuerpo

int sig_y, sig_x;
int key = ERR;
bool terminar = FALSE;
int max_x_estado,max_y_estado,largo_menu_principal;
int ID;
WINDOW *Wmenu, *Wtitulo, *Wbody, *Westado;
char fecha_actual[50];

static int entrada_teclado(WINDOW *ventana,char **ptrp,int largo_max,int pass);

void inicializar()
{
    initscr(); //inicializa pdcurses
    start_color(); //activa coloroes
    // fuente, fondo
    //resizeterm(60,150);
    #ifdef _WIN32
    resize_term(60,150);
    #endif
    init_pair(1, COLOR_BLUE, COLOR_WHITE);//COLOR_TITULO
    init_pair(2, COLOR_BLUE, COLOR_WHITE); //COLOR_MENU_PRINCIPAL
    init_pair(3, COLOR_RED, COLOR_BLUE); //COLOR_MENU_PRINCIPAL_SEL
    init_pair(4, COLOR_BLUE, COLOR_WHITE);//COLOR_SUB_MENU
    init_pair(5, COLOR_WHITE, COLOR_BLACK); //COLOR_SUB_MENU_SEL
    init_pair(6, COLOR_WHITE, COLOR_BLUE);//COLOR_CUERPO , COLOR_BLUE
    init_pair(7, COLOR_RED, COLOR_WHITE);//COLOR_BARRA_ESTADO,
    init_pair(8, COLOR_BLUE, COLOR_WHITE);//INPUTBOXCOLOR
    init_pair(9, COLOR_WHITE, COLOR_BLACK);//EDITBOXCOLOR
    init_pair(10,COLOR_BLUE,COLOR_RED); //COLOR_ERROR
    init_pair(11,COLOR_GREEN,COLOR_WHITE); //COLOR_PREG
    init_pair(12,COLOR_WHITE,COLOR_BLACK); //COLOR_ETIQ
    // inicializa las subventanas
    Wtitulo = subwin(stdscr, at, bw, 0, 0);
    Wmenu = subwin(stdscr, am, bw, at, 0);
    Wbody = subwin(stdscr, ac, bw, at + am, 0);
    Westado = subwin(stdscr, ae, bw, at + am + ac, 0);

    caja_color(Wtitulo, COLOR_TITULO, 0);
    caja_color(Wmenu, COLOR_MENU_PRINCIPAL, 0);
    caja_color(Wbody, COLOR_CUERPO, 0);
    caja_color(Westado, COLOR_BARRA_ESTADO, 0);

    cbreak();
    noecho();              //no muestra las teclas que presiono
    curs_set(0);           //oculto el cursor
    keypad(Wbody, TRUE);   //activa las flechas del teclado
    scrollok(Wbody, TRUE); //se puede desplazar en pantalla Wbody

    leaveok(stdscr, TRUE); //deja el cursor donde quedo no ma
    leaveok(Wtitulo, TRUE);
    leaveok(Wmenu, TRUE);
    leaveok(Westado, TRUE);
}

void iniciar_menu(menu *mp,int tipo_usuario)
{
    ID=tipo_usuario;
    if(ID!=ERR)
        menu_principal(mp);
    finalizar();
}

void msg_cuerpo(char *msg)
{
    waddstr(Wbody, msg);
    wrefresh(Wbody);
}

void mostrar_menu(menu *mp)
{
    int y, x, nitems, largo_menu, alto_menu, ancho_menu, pos_ant = -1, pos_ac= 0;
    bool stop = FALSE;
    WINDOW *wmenu;
    curs_set(0);
    obt_pos_menu(&y, &x);
    mp=menu_mod(mp);
    dimen_smenu(mp, &nitems, &largo_menu);
    if(largo_menu<(largo_menu_principal-2))
        largo_menu=largo_menu_principal-2;
    alto_menu = nitems + 2;
    ancho_menu = largo_menu + 2;
    wmenu = newwin(alto_menu, ancho_menu, y, x);
    caja_color(wmenu, COLOR_SUB_MENU, 1);
    act_menu(wmenu, mp);
    key = ERR;
    while (!stop && !terminar)
    {
        if (pos_ac != pos_ant)
        {
            if (pos_ant != -1)
                mvwaddstr(wmenu, pos_ant+ 1, 1,prepad(padstr(mp[pos_ant].nombre, largo_menu - 1), 1));

            setear_color(wmenu, COLOR_SUB_MENU_SEL);
            mvwaddstr(wmenu, pos_ac + 1, 1,prepad(padstr(mp[pos_ac].nombre, largo_menu - 1), 1));

            setear_color(wmenu, COLOR_SUB_MENU);
            msg_estado(mp[pos_ac].descr);

            pos_ant = pos_ac;
            wrefresh(wmenu);
        }
        switch (key = wgetch(Wbody))
        {
        case '\n':
            touchwin(Wbody);
            wrefresh(Wbody);
            fijar_pos_menu(y + 1, x + 1);
            key = ERR;
            curs_set(1);
            (mp[pos_ac].func)();   //llama a la funcion del menu correspondiente
            curs_set(0);
            act_menu(wmenu, mp);
            pos_ant = -1;
            break;
        case KEY_UP:
            pos_ac = (pos_ac + nitems - 1) % nitems;
            key = ERR;
            break;
        case KEY_DOWN:
            pos_ac = (pos_ac + 1) % nitems;
            key = ERR;
            break;
        case KEY_ESC:
        case KEY_LEFT:
        case KEY_RIGHT:
            if (key == KEY_ESC)
                key = ERR;  //regresar al menu anterior
            stop = TRUE;
            break;
#ifndef __linux__
        case ALT_X:
            terminar=1;
            break;
#endif
        default:
            pos_ac=pos_ant;
            key=ERR;
        }
    }
    free(mp);
    delwin(wmenu);
    touchwin(Wbody);
    wrefresh(Wbody);
}

void caja_color(WINDOW *win, chtype color, int caja) //REVISAR Y LIMPIAR CODIGO
{
    int maxy;
    setear_color(win, color);
    wbkgd(win, COLOR_PAIR(color));
    werase(win);
    maxy = getmaxy(win);
    if (caja && (maxy > 2))
        box(win, 0, 0);
    touchwin(win);
    wrefresh(win);
}

void fijar_pos_menu(int y, int x)
{
    sig_y = y;
    sig_x = x;
}

void obt_pos_menu(int *y, int *x)
{
    *y = sig_y;
    *x = sig_x;
}

//Pone un mensaje en la barra de estado
void msg_estado(char *msg)
{
    werase(Westado);
    mvwaddstr(Westado, 0,(bw-strlen(msg))/2 , msg);
    wrefresh(Westado);
}

void titulo(char * tipo,char * nombre)
{
    wprintw(Wtitulo,"LitERP - Tipo Usuario: %s - Nombre: %s - %s",tipo,nombre,lafecha(1,0));
    wrefresh(Wtitulo);
}

void menu_principal(menu *mp)
{
    int nitems, pos_ant = -1, pos_ac = 0, c,i;
    int largo_mp[20],largo[20];
    mp=menu_mod(mp);
    dimen_menu(mp, &nitems, largo_mp);
    largo[0]=0;
    for(i=1;i<=nitems;i++)
        largo[i]=largo_mp[i-1]+largo[i-1];
    act_menu_principal(largo_mp, mp);
    while (!terminar)
    {
        if (pos_ac != pos_ant)
        {
            if (pos_ant != -1)
            {
                mvwaddstr(Wmenu, 0, largo[pos_ant],prepad(padstr(mp[pos_ant].nombre, largo_mp[pos_ant] - 1), 1));
                msg_estado(mp[pos_ac].descr);
            }
            else
                msg_estado("Use las flechas del teclado para navegar por los menus");
            setear_color(Wmenu, COLOR_MENU_PRINCIPAL_SEL);
            mvwaddstr(Wmenu, 0, largo[pos_ac],prepad(padstr(mp[pos_ac].nombre, largo_mp[pos_ac] - 1), 1));
            setear_color(Wmenu, COLOR_MENU_PRINCIPAL);
            pos_ant = pos_ac;
            wrefresh(Wmenu);
        }
        switch (c = wgetch(Wbody))
        {
        case KEY_DOWN:
        case '\n':
            touchwin(Wbody);
            wrefresh(Wbody);
            largo_menu_principal=largo_mp[pos_ac];
            fijar_pos_menu(at + am, largo[pos_ac]);
            curs_set(1);
            (mp[pos_ac].func)();//va a la funcion correspondiente
            curs_set(0);
            switch (key)
            {
            case KEY_LEFT:
                pos_ac = (pos_ac + nitems - 1) % nitems;
                key = '\n';
                break;
            case KEY_RIGHT:
                pos_ac = (pos_ac + 1) % nitems;
                key = '\n';
                break;
            default:
                key = ERR;
            }
            act_menu_principal(largo_mp, mp);
            pos_ant = -1;
            break;
        case KEY_LEFT:
            pos_ac = (pos_ac + nitems - 1) % nitems;
            break;
        case KEY_RIGHT:
            pos_ac = (pos_ac + 1) % nitems;
            break;
        case KEY_ESC:
            msg_estado("Use las flechas del teclado y presione Enter para seleccionar");
            break;
        default:
            pos_ac=pos_ant;
            key=ERR;
        }

    }
    touchwin(Wbody);
    wrefresh(Wbody);
    free(mp);
}

void act_menu_principal(int *ancho, menu *mp)
{
    int i,an=0;
    menu *p = mp;
    for (i = 0; p->func; i++, p++)
    {
        an+=i>0?ancho[i-1]:0;
        mvwaddstr(Wmenu, 0, an, prepad(padstr(p->nombre, ancho[i] - 1), 1));
    }
    touchwin(Wmenu);
    wrefresh(Wmenu);
}

//obtiene numero de menus y numero de caracteres del menu mas largo
void dimen_smenu(menu *mp, int *nmenus, int *maxcarac)
{
    int n, l, mmax = 0;
    for (n=0; mp->func; n++, mp++)
        if ((l = strlen(mp->nombre)) > mmax) mmax = l;
    *nmenus = n; //numero de menus
    *maxcarac = mmax + 2; //mmax, numero carac del menu mas largo
}

//Arriba version original
void dimen_menu(menu *mp, int *nmenus, int *maxcarac)
{
    int n;
    for (n=0; mp->func; n++, mp++)
        maxcarac[n]= strlen(mp->nombre)+2;
    *nmenus = n; //numero de menus
}

void act_menu(WINDOW *wmenu, menu *mp)
{
    int i;
    menu *p = mp;
    for (i = 0; p->func; i++, p++)
        mvwaddstr(wmenu, i + 1, 2, p->nombre);
    touchwin(wmenu);
    wrefresh(wmenu);
}

//rellena con espacios caract faltantes
char *padstr(char *s, int largo)
{
    static char temp[50];
    char formato[10];
    sprintf(formato, (int)strlen(s) > largo ? "%%.%ds" : "%%-%ds", largo);
    sprintf(temp, formato, s);
    return temp;
}

//
char *prepad(char *s, int length)
{
    int i;
    char *p = s;
    if (length > 0)
    {
        memmove((void *)(s + length), (const void *)s, strlen(s) + 1);
        //mueve s a s+length
        for (i = 0; i < length; i++)
            *p++ = ' ';//agrega espacio al principio de s
    }
    return s;
}

void setear_color(WINDOW *win, chtype color)
{
    chtype attr = color & A_ATTR;
    attr &= ~A_REVERSE;
    wattrset(win, COLOR_PAIR(color & A_CHARTEXT) | attr);
}

WINDOW *ventana_principal(void)
{
    return Wbody;
}

void salir(int si)
{
    terminar=si;
}

/*  recibe vector de preguntas, el largo maximo de las resp
    y el num de respues que deben ser ocultadas con asteriscos,
    contadas en orden inverso */
char ** obtener_datos(char *preg[],char * desc,int largo_max,int pass)
{
    WINDOW *Win,*Wcampo;
    char **resp,*msg;
    int opcion;
    int n,max=0,temp,wancho,walto,salir=0,posy=0,pos_ant=0;
    for (n = 0; preg[n]; n++) //mmax: largo mas grande en preg
        if ((temp = strlen(preg[n])) > max)
            max = temp;
    wancho=max+largo_max+5; // | <-max-> <-largo_max-> |
    walto=n+2;
    Win=newwin(walto,wancho,(LINES-walto)/2,(COLS-wancho)/2);
    caja_color(Win,INPUTBOXCOLOR,1);
    for(temp=0; temp<n; temp++) //imprime oerguntas
        mvwprintw(Win,1+temp,2,preg[temp]);
    resp=(char **) calloc((n+1),sizeof(char*));
    wrefresh(Win);
    if(!pass)
    {
        msg=(char *)malloc((strlen(desc)*sizeof(char)+22));
        strcpy(msg,desc);
        strcat(msg,", ESC para cancelar");
        msg_estado(msg);
    }
    else msg_estado(desc);
    keypad(Win,1);
    while(!salir)
    {
        wrefresh(Win);
        Wcampo=subwin(Win,1,largo_max,(LINES-walto)/2+posy+1,(COLS-wancho)/2+max+3);
        keypad(Wcampo,1);
        caja_color(Wcampo, EDITBOXCOLOR, 0);
        curs_set(1);
        temp=pass?((posy)>=(n-pass)?1:0):0;
        switch((opcion=entrada_teclado(Wcampo,resp+posy,largo_max,temp)))
        {
        case KEY_UP:
            pos_ant=posy;
            posy=posy?(posy-1)%n:n-1;
            break;
        case KEY_DOWN:
            pos_ant=posy;
            posy=(posy+1)%n;
            break;
        case KEY_ENTER:
            pos_ant=posy;
            if(posy==(n-1))
                salir=TRUE;
            else
                posy=(posy+1)%n;
            break;
        case KEY_ESC:
            liberar_char_array(resp);
            resp=NULL;
            salir=TRUE;
            break;
        }
        curs_set(0);
        caja_color(Wcampo,INPUTBOXCOLOR,0);
        werase(Wcampo);
        delwin(Wcampo);
        if(opcion!=KEY_ESC)
        {//imprime los *
            if(temp)
            {
                for(temp=0; resp[pos_ant][temp]; temp++)
                    mvwprintw(Win, pos_ant+1, temp+max+3, "%c",'*');
            }
            else  mvwprintw(Win, pos_ant+1, max+3, "%s",resp[pos_ant]);
        }
        touchwin(Win);
        wrefresh(Win);
    }
    delwin(Win);
    touchwin(Wbody);
    wrefresh(Wbody);
    curs_set(0);
    return resp;
}

char ** obtener_mod_datos(char *preg[],char *resp[],char * desc,int largo_max,int pass)
{
    WINDOW *Win,*Wcampo;
    char *msg;
    //char **resp
    int opcion;
    int n,max=0,temp,wancho,walto,salir=0,posy=0,pos_ant=0;
    for (n = 0; preg[n]; n++) //mmax: largo mas grande en preg
        if ((temp = strlen(preg[n])) > max)
            max = temp;
    wancho=max+largo_max+5; // | <-max-> <-largo_max-> |
    walto=n+2;
    Win=newwin(walto,wancho,(LINES-walto)/2,(COLS-wancho)/2);
    caja_color(Win,INPUTBOXCOLOR,1);
    for(temp=0; temp<n; temp++) //imprime oerguntas
        mvwprintw(Win,1+temp,2,preg[temp]);
    if(!resp)
        resp=(char **) calloc((n+1),sizeof(char*));
    else
        for(temp=0;preg[temp]!=NULL;temp++)
            mvwprintw(Win,1+temp,3+max,resp[temp]);
    wrefresh(Win);
    if(!pass)
    {
        msg=(char *)malloc((strlen(desc)*sizeof(char)+22));
        strcpy(msg,desc);
        strcat(msg,", ESC para cancelar");
        msg_estado(msg);
    }
    else msg_estado(desc);
    while(!salir)
    {
        wrefresh(Win);
        Wcampo=subwin(Win,1,largo_max,(LINES-walto)/2+posy+1,(COLS-wancho)/2+max+3);
        keypad(Wcampo,1);
        caja_color(Wcampo, EDITBOXCOLOR, 0);
        curs_set(1);
        temp=pass?((posy)>=(n-pass)?1:0):0;
        switch((opcion=entrada_teclado(Wcampo,resp+posy,largo_max,temp)))
        {
        case KEY_UP:
            pos_ant=posy;
            posy=posy?(posy-1)%n:n-1;
            break;
        case KEY_DOWN:
            pos_ant=posy;
            posy=(posy+1)%n;
            break;
        case KEY_ENTER:
            pos_ant=posy;
            if(posy==(n-1))
                salir=TRUE;
            else
                posy=(posy+1)%n;
            break;
        case KEY_ESC:
            liberar_char_array(resp);
            resp=NULL;
            salir=TRUE;
            break;
        }
        curs_set(0);
        caja_color(Wcampo,INPUTBOXCOLOR,0);
        werase(Wcampo);
        delwin(Wcampo);
        if(opcion!=KEY_ESC)
        {//imprime los *
            if(temp)
            {
                for(temp=0; resp[pos_ant][temp]; temp++)
                    mvwprintw(Win, pos_ant+1, temp+max+3, "%c",'*');
            }
            else  mvwprintw(Win, pos_ant+1, max+3, "%s",resp[pos_ant]);
        }
        touchwin(Win);
        wrefresh(Win);
    }
    delwin(Win);
    touchwin(Wbody);
    wrefresh(Wbody);
    curs_set(0);
    return resp;
}

//devuelve los menus que tienen req>=ID
menu *menu_mod(menu * mp)
{
    menu *menu2;
    int num_items_validos=0,nitem_total,i,j;
    //Copia los elementos que corresponden con el tipo de usuario a otro menu
    for(nitem_total=0; mp[nitem_total].func; nitem_total++)
        if(ID<=mp[nitem_total].req)
            num_items_validos++;
    menu2=(menu *)malloc((num_items_validos+1)*sizeof(menu));
    for(i=0,j=0; i<=nitem_total; i++)
        if((ID<=mp[i].req)||(mp[i].func==NULL))
        {
            menu2[j]=mp[i];
            j++;
        }
    return menu2;
}

//pass= 1 muestra *
static int entrada_teclado(WINDOW *ventana,char **ptrp,int largo_max,int pass)
{
    int i=0,j,c;
    char *t;
    keypad(ventana, TRUE);
    if(*ptrp==NULL)
        *ptrp=(char *) malloc(sizeof(char));
    else
    {
        i=strlen(*ptrp);
        if(pass)
        {
            for(j=0; j<i; j++)
                wprintw(ventana,"%c",'*');
        }
        else waddstr(ventana,*ptrp);
    }
    //Lee el teclado
    while((c=wgetch(ventana))!=ENTER) //10=enter
    {
        if(c==BACKSPACE) //ascii 8 es tecla <-- (borra)
        {
            i--;
            if(i<0)
            {
                i=0;
                continue;
            }
            wmove(ventana,0,i);
            waddstr(ventana," ");
            wmove(ventana,0,i);
        }
        else if((c==KEY_DOWN)||(c==KEY_UP)||(c==KEY_ESC)||(c==ALT_X))
        {
            if(c!=KEY_ESC)
                (*ptrp)[i]='\0';
            return c;
        }
        else if((c!=9)&&(c!=KEY_LEFT)&&(c!=KEY_RIGHT)) //ignora los tab
        {
            waddch(ventana,pass?'*':c);
            if(i<largo_max)
            {
                i++;
                t=*ptrp; //se guarda la direccion del vector con los caracteres anteriores en t
                *ptrp=(char *) malloc((i+1)*sizeof(char)); //pide memoria para un caracter mas
                (*ptrp)[i-1]=c; //penultimo caracter = c, ultimo debe ser '\0'
                for(j=0; j<(i-1); j++)
                {
                    //copia los caracteres desde t a ptrp
                    ptrp[0][j]=t[j];
                }
                free(t);
            }else (*ptrp)[i-1]=c;
        }
    }
    (*ptrp)[i]='\0';
    return KEY_ENTER;//10;//retorna enter
}

void finalizar()
{
    delwin(Wtitulo);
    delwin(Wmenu);
    delwin(Wbody);
    delwin(Westado);
    curs_set(1);
    endwin();
}

int preg_error(char * msg, int tipo)
{ //tipo >0, es error, tipo 0 es preg
    WINDOW * Werror,*Werror2;
    chtype color;
    int i,j=0,k,maxy=0,maxx=0,ch;
    for(i=0; msg[i]!='\0'; i++)
    {
        k=0;
        for(j=i; msg[j]!='\0'&&msg[j]!='\n'; j++,k++);
        k>maxx?maxx=k:k;
        maxy++;
        if(msg[j]=='\0')
            i=j-1;
        else i=j;
    }
    if(tipo>0)
    {
        beep();
        color=COLOR_ERROR;
    }
    else color=COLOR_PREG;
    maxy+=4;
    maxx+=4;
    Werror=newwin(maxy,maxx,(LINES-maxy)/2,(COLS-maxx)/2);
    Werror2=subwin(Werror,maxy-2,maxx-2,(LINES-maxy)/2+1,(COLS-maxx)/2+1);
    caja_color(Werror,color,1);
    caja_color(Werror2,color,0);
    mvwaddstr(Werror2,1,1,msg);
    touchwin(Werror2);
    wrefresh(Werror2);
    curs_set(0);
    ch=wgetch(Werror2);
    delwin(Werror2);
    delwin(Werror);
    touchwin(Wbody);
    wrefresh(Wbody);
    return ch;
}

char * lafecha(int fecha, int hora)
{
    time_t tiempo = time(0);
    struct tm *tlocal = localtime(&tiempo);
    char formato[20];
    sprintf(formato,"%s%s",fecha?"%d/%m/%y":"",hora?"%H:%M:%S":""); //"
    strftime(fecha_actual,50,formato,tlocal);
    return fecha_actual;
}

WINDOW * obt_win_cuerpo(void)
{
    return Wbody;
}
